/*================================================================
 *  txt2s.c
 *               Convert a text to assembly source
 *
 *================================================================
 *
 * 25thanni, a demo dedicated to the 25th anniversary of the ZX81.
 *
 * (c)2006 Bodo Wenzel
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the Free
 * Software Foundation Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *================================================================
 */

/* This is a classical filter, reading from stdin and writing to
 * stdout. The input is a text file, the output is ment to be
 * included into an ASxxxx source.
 */

/* Externals ================================================== */

#include <stdlib.h>
#include <stdio.h>

#include "pack.h"

/* Constants ================================================== */

#define COMMENT '#'
#define GFX     '['
#define INV     '^'
#define NL      '!'

#define ZX_INV 0x80
#define ZX_NL  0xC9

#define UNPACKED_SIZE 2048
#define PACKED_SIZE   2048

/* Variables ================================================== */

static const char zx[] = {
  ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', /* 0x00 .. 0x07 */
  ' ', ' ', ' ', '"', '|', '$', ':', '?', /* 0x08 .. 0x0F */
  '(', ')', '>', '<', '=', '+', '-', '*', /* 0x10 .. 0x17 */
  '/', ';', ',', '.', '0', '1', '2', '3', /* 0x18 .. 0x1F */
  '4', '5', '6', '7', '8', '9', 'A', 'B', /* 0x20 .. 0x27 */
  'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0x28 .. 0x2F */
  'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', /* 0x30 .. 0x37 */
  'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', /* 0x38 .. 0x3F */
};

static size_t        unpacked_len;
static unsigned char unpacked[UNPACKED_SIZE];
static size_t        packed_len;
static unsigned char packed[PACKED_SIZE];

/* Prototypes ================================================= */

static void
quit(const char *message);

static int
get_character(void);

static void
put_gfx(int c, int inv);

static void
put_normal(int c, int inv);

/* Program code =============================================== */

/* Main function ---------------------------------------------- */

int main(void) {
  unsigned int comment = 0;
  unsigned int gfx = 0;
  unsigned int inv = 0;
  int          i;

  for (;;) {
    int c;

    c = get_character();
    if (c == EOF) {
      break;
    }

    if (comment) {
      if (c == '\n') {
	comment = 0;
      }

    } else if (gfx) {
      put_gfx(c, inv);
      inv = 0;
      gfx = 0;

    } else {
      switch (c) {
      case COMMENT:
	comment = 1;
	break;
      case '\t':
      case '\r':
      case '\n':
	break;
      case GFX:
	gfx = 1;
	break;
      case INV:
	inv = ZX_INV;
	break;
      case NL:
	unpacked[unpacked_len++] = ZX_NL;
	break;
      default:
	put_normal(c, inv);
	inv = 0;
	break;
      }
    }

    if (unpacked_len > UNPACKED_SIZE) {
      quit("Buffer overflow in unpacked data");
    }
  }

  /* pack and save all data ----------------------------------- */

  packed_len = pac(PACKED_SIZE, packed, unpacked_len, unpacked);
  if (packed_len == 0) {
    quit("Buffer overflow in packed data");
  }
  printf("UNPACKED\t=\t%u\n", unpacked_len);
  for (i = 0; i < (int)packed_len; i++) {
    printf("\t.db\t0x%02X\n", packed[i]);
  }

  return 0;
}

/* Report a message and exit the program ---------------------- */

static void
quit(const char *message) {
  fputs(message, stderr);
  fputs("!\n", stderr);
  exit(EXIT_FAILURE);
}

/* Obtain a single character ---------------------------------- */

static int
get_character(void) {
  int c;

  c = fgetc(stdin);
  if (c == EOF) {
    if (ferror(stdin)) {
      quit("Read error");
    }
  }
  return c;
}

/* Save a graphic character ----------------------------------- */

static void
put_gfx(int c, int inv) {
  char line[25];

  switch (c) {
  case '0':
    unpacked[unpacked_len++] = 0x00;
    break;
  case '1':
    unpacked[unpacked_len++] = 0x01;
    break;
  case '2':
    unpacked[unpacked_len++] = 0x02;
    break;
  case '3':
    unpacked[unpacked_len++] = 0x03;
    break;
  case '4':
    unpacked[unpacked_len++] = 0x04;
    break;
  case '5':
    unpacked[unpacked_len++] = 0x05;
    break;
  case '6':
    unpacked[unpacked_len++] = 0x06;
    break;
  case '7':
    unpacked[unpacked_len++] = 0x07;
    break;
  case '8':
    unpacked[unpacked_len++] = 0x87;
    break;
  case '9':
    unpacked[unpacked_len++] = 0x86;
    break;
  case 'A':
    unpacked[unpacked_len++] = 0x85;
    break;
  case 'B':
    unpacked[unpacked_len++] = 0x84;
    break;
  case 'C':
    unpacked[unpacked_len++] = 0x83;
    break;
  case 'D':
    unpacked[unpacked_len++] = 0x82;
    break;
  case 'E':
    unpacked[unpacked_len++] = 0x81;
    break;
  case 'F':
    unpacked[unpacked_len++] = 0x80;
    break;
  case 'f': /* full */
    unpacked[unpacked_len++] = inv | 0x08;
    break;
  case 'l': /* lower */
    unpacked[unpacked_len++] = inv | 0x09;
    break;
  case 'u': /* upper */
    unpacked[unpacked_len++] = inv | 0x0a;
    break;
  default:
    sprintf(line, "Unknown character after %c (%c)", GFX, c);
    quit(line);
    break;
  }
}

/* Save a normal character ------------------------------------ */

static void
put_normal(int c, int inv) {
  unsigned int k;

  for (k = 0; k < sizeof(zx) / sizeof(char); k++) {
    if (c == zx[k]) {
      break;
    }
  }

  if (k >= sizeof(zx)/sizeof(char)) {
    char line[25];

    sprintf(line, "Unknown character (%c)", c);
    quit(line);
  }
  unpacked[unpacked_len++] = inv | k;
}

/* The end ==================================================== */
